import urlparse
import urllib
import cgi

from paste.httpheaders import CONTENT_LENGTH
from paste.httpheaders import CONTENT_TYPE
from paste.httpheaders import LOCATION
from paste.httpexceptions import HTTPFound
from paste.httpexceptions import HTTPUnauthorized

from paste.request import parse_dict_querystring
from paste.request import parse_formvars
from paste.request import construct_url

from zope.interface import implements
from repoze.who.interfaces import IChallenger
from repoze.who.interfaces import IIdentifier

from pylons.templating import render


class FormPluginBase(object):
    def _get_rememberer(self, environ):
        rememberer = environ['repoze.who.plugins'][self.rememberer_name]
        return rememberer

    # IIdentifier
    def remember(self, environ, identity):
        rememberer = self._get_rememberer(environ)
        return rememberer.remember(environ, identity)

    # IIdentifier
    def forget(self, environ, identity):
        rememberer = self._get_rememberer(environ)
        return rememberer.forget(environ, identity)

    def __repr__(self):
        return '<%s %s>' % (self.__class__.__name__, id(self))

class FormPlugin(FormPluginBase):

    implements(IChallenger, IIdentifier)

    def __init__(self, login_name, password_name, rememberer_name,
                 logout_handler_path,
                 formtemplate=None, formcallable=None):
        self.login_name = login_name
        self.password_name = password_name
        # rememberer_name is the name of another configured plugin which
        # implements IIdentifier, to handle remember and forget duties
        # (ala a cookie plugin or a session plugin)
        self.logout_handler_path = logout_handler_path
        self.rememberer_name = rememberer_name
        self.formtemplate = formtemplate
        self.formcallable = formcallable
        
    # IIdentifier
    def identify(self, environ):
        path_info = environ['PATH_INFO']
        query = parse_dict_querystring(environ)

        # Let say for now we always assume we look if the user want to log in.
        if environ['REQUEST_METHOD'] == 'POST':
            form = parse_formvars(environ)
            from StringIO import StringIO
            # XXX we need to replace wsgi.input because we've read it
            # this smells funny
            #environ['wsgi.input'] = StringIO()
            try:
                login = unicode(form[self.login_name])
                password = unicode(form[self.password_name])
            except KeyError:
                return None
            environ['QUERY_STRING'] = urllib.urlencode(query)
            # the request becomes a GET and move on
            environ['REQUEST_METHOD'] = 'GET'
            return {'login':login, 'password':password}
        return None
      
    # IChallenger
    def challenge(self, environ, status, app_headers, forget_headers):
        if app_headers:
            location = LOCATION(app_headers)
            if location:
                headers = list(app_headers) + list(forget_headers)
                return HTTPFound(headers = headers)

        if self.formcallable is not None:
            form = self.formcallable(environ)
        else:
            # We can use this because at this point we are pretty sure 
            # we have been up to the stack where the rendering engine
            # has been registered.
            form = render(self.formtemplate, login_name=self.login_name, 
                          password_name=self.password_name)
        def auth_form(environ, start_response):
            content_length = CONTENT_LENGTH.tuples(str(len(form)))
            content_type = CONTENT_TYPE.tuples('text/html')
            headers = content_length + content_type + forget_headers
            start_response('401 Unauthorized', headers)
            return [form]

        return auth_form


def make_plugin(login_name, password_name,
                rememberer_name=None, logout_handler_path=None, form=None):
    if rememberer_name is None:
        raise ValueError(
            'must include rememberer key (name of another IIdentifier plugin)')
    if logout_handler_path is None:
        raise ValueError(
            'must include logout handler path')
    plugin = FormPlugin(login_name, password_name, rememberer_name, 
                        logout_handler_path, form)
    return plugin
